home *** CD-ROM | disk | FTP | other *** search
- Assembly Language for Veggies (And C programmers) Part 5.
-
- Welcome to 1992! It is now several months since the last ASMVEG series... In
- this time I have been learning a bit about Graphics on the PC, and mucking
- about with Turbo Pascal, however it has now come time to again put words
- onscreen about dear old Assembler once more...
-
- Last issue, we looked at The TSR, and started mentioning some tricky things
- that are important to programming TSRs... This issue i'd like to delve into
- some simpler work by presenting some simple routines for doing "Workhorse"
- stuff - ya know, stuff you take for granted like screen clearing and file
- reading/writing etc etc which everyone always seems to assume you know..
-
- Oh, if you have a WordProcessor there with adjustable tabstops, set them to 20
- spaces between tabs or the listings will look shitty!
-
- OK, here goes...
-
-
- Screen Clear
- ------------
-
- A simple routine for a simple job: Clears the screen in the current screen
- mode. Homes cursor on text screens, homes ligical cursor on graphics screens.
-
- Clear_screen: push ax
- mov ah,0fh
- int 010h
- mov ah,0
- int 010h
- pop ax
- ret
-
- Very easy! Does all the usual jazz like resetting the colours to their
- defaults etc. This is a standard sort of thing that I use in just about every
- program I can ever remember writing... it works, it's reliable, and let's
- face it, who wants to clear the screen faster??
-
-
- Go "Beep"
- ---------
-
- Yet another rediculous task. Make a beep! This beep is the standard PC beep,
- such as you get when you type a text file full of CTRL-G's to the screen or
- fill uo your keyboard buffer, and is instantly recognisable as the work of the
- BIOS..
-
- Go_Beep: push ax
- push bx
- mov ah,0eh
- mov al,07
- mov bh,0
- int 010h
- pop bx
- pop ax
- ret
-
- Call this as often as you like, but remember that your whole program is paused
- for the length of the tone.
-
-
- What Video?
- -----------
-
- Ever get sick of asking users their video card type? Why not auto-detect it
- yourself and TELL them what you detected... simple as pie! These video bios
- functions were built in to assist the programmer and take the hassle of asking
- the user what his card is.. buggered if I know why game writers don't just use
- this stuff themselves.... maybe they're stupid C programmers?
-
- Detect_VGA: mov ax,01a00h
- int 010h
- cmp al,01ah
-
- ( if AL=0a1h, then VGA is present )
-
- If you have a MCGA (PS/2) system, this will detect the MCGA. To differentiate
- between MCGA and true VGA is a difficult thing.. if you specificly want to do
- something other than 320x200x256 mode then you'll be needing to detect a SVGA
- anyhow, so it doesn't really matter.
-
- Detect_EGA: mov ah,012h
- mov bl,010h
- int 010h
- cmp bl,010h
-
- ( if BL=010h then EGA is NOT present )
-
- NB: if BL is not 010h, then it will indicate this:
-
- BL = 0, 64k EGA RAM
- BL = 1, 128k EGA RAM
- BL = 2, 192k EGA RAM
- BL = 3, 256k EGA RAM
-
- WARNING: Use this AFTER Detect_VGA because a VGA will also return this
- information as well (After all, a vGA emulates EGA don't it?!?!)
-
- OK.. if it's not EGA or VGA then it MUST be either CGA or MONO ... if it's a
- mone, the screen MUST be in mode 7, if it's CGA it will be in another mode..
-
- Detect_CGA_MONO: mov ah,0fh
- int 010h
- cmp al,7
-
- ( if AL=7, MONO is present, else must be CGA )
-
- Furthermore: if BOTH a MONO and ANY colour card are present, and you want to
- make sure, first detect the Default card... if you find a colour card, try
- calling setvideomode and try setting mode 7. Next, read the mode back. if
- you're in mode 7, MONO is present. if not, then you only got colour. If you
- detect MONO as default and you don't find VGA or EGA, try setting mode 3 to
- test for CGA. If you can set mode 3 then CGA is present, but not default.
-
-
- KeyPressed?
- -----------
-
- Moving right along.... the BIOS has some good keypress routines indeed,
- in fact I have never needed to do anything keyboardish and not been able to
- use the BIOS for it... oh, and the BIOS is very quick with the keyboard too -
- definately suitable for use where high speed is required.
-
- Is_A_Key_Pressed: mov ah,1
- int 016
-
- If the Z (Zero) flag is set, then NO keys have been pressed. If the Z flag is
- NOT set a key has been pressed. This does not retrieve a key from the keyboard
- buffer, it just lets you know there's one there to be read later on.
-
-
- Next Key in Buffer
- ------------------
-
- Retrieve_keypress: mov ah,0
- int 016
-
- This gets the next key that has been pressed and puts the "Main byte" in AL,
- and the "Auxiliary Byte" in AH. normally AL holds the ASCII code for the key
- pressed, but if the key is not a normal key (like a function or arrow key)
- then AL=0 and AH holds the so called EXTENDED keycode... tables of extended
- keycodes should be in any good ASM book - Norton's has it for one. Note, if
- you call this service BEFORE a key is ready, the routine pauses till one key
- is hit, which freezes the program in effect... saves a look for key, until
- pressed, then read key loop though.
-
-
- Keyboard Lights (AT Computers ONLY!)
- ------------------------------------
-
- Ever wanted to stuff with the keyboard lights? Once again the BIOS will help
- us there... At absolute memory location 0040:0017 - that's right - the BIOS
- data table...
-
- Here is the layout of that byte:
-
- bit# 7 6 5 4 3 2 1 0
- Insert Caps Num Scroll ALT CTRL L.SHI R.SHI
-
- Altering any of these bits will alter the computer's idea of what's what...
- changing bits 6-4 will toggle the 3 lights on the KB.. all courtesy of the
- BIOS.. neat and quick... Fiddling with INS changes the INS state, but mucking
- with alt, ctrl and the shifts is not on, otherwise you REVERSE the action of
- that key; held down becomes like released etc... if you turned them all
- opposite to what they normally are, then to type normally one would have to
- hold down ALT, CTRL and both shift keys!!
-
-
- Reading a file to memory
- ------------------------
-
- One of the hardest routines to master is file access. Here it is simplified.
-
- Filename db 'filexxxx.!!!',0
- Handle dw 0
- memseg dw 07000h
-
- Load_File: mov ah,03dh
- mov al,0
- mov dx,offset filename
- int 021h
-
- This section opens the file for Read/Write access... changing AL changes the
- access thus:
-
- AL = 0 Read/Write
- AL = 1 Write Only
- AL = 2 Read Only
-
- Obviously the filename is held in an ASCIIZ string at Filename. ASCIIZ stands
- for ASCII with a 0 on the end basicly.. the 0 tells DOS where the end of the
- string is.
-
- jc FAIL
-
- If carry is set on return, the open operation FAILED. The file must exist and
- be able to be opened for this operation to work. An error code will be passed
- in AX - again see Norton's or other for errorcode listings.
-
- mov handle,ax
-
- DOS has what is called a handle passed into AX if the open was successfull..
- each file has a unique handle, and there may be up to the number of files
- liste din the FILES= statement in Config.sys open at any one time. note that
- StdIn, StdOut, StdErr and Null are always open by DOS and are used when
- "piping" program outpout to and from files. (DIR > files.lst for example) so
- normally the handle for the fist open file will be 0005. We must store
- whatever we get back in any case so this is done.
-
- At this stage we have simply requested DOS to get us a file. We haven't read
- it yet.
-
- read_from_file: mov dx,word ptr cs:[memseg]
- push dx
- pop ds
- mov dx,0
- mov ah,03fh
- mov bx,word ptr cs:[handle]
- mov cx,0ffffh
- int 021h
- jc FAIL
- cmp ax,0ffffh
- jnz end_read
-
- mov dx,word ptr cs:[memseg]
- add dx,01000h
- mov word ptr cs:[memseg],dx
- jmp read_from_file
-
- end_read: push cs
- pop ds
-
- Now this is quite complex and takes a while to understand.... read up on how
- the registers are bieng used... basicly the file is loaded in 64k chunks
- (Because that's the biggest DOS can do in one go) to the 64k chunk of memory
- located at DS:DX. This program happens to hard code the load address at 07000h
- but normally you'd change that to the place you'd requested from DOS. It also
- assumes to start at offset 0 in that segment... change DX if you start part
- way into the segment. The routine attempts to load 64k; and will load up to
- the limit of a less-than-64k file. Again, C is set if there is an error same
- as the open routine. Next, AX holds the bytes read. if AX is less than 0ffffh
- then less than 64k was read and we're at the end of the file. If so, jump out.
- If not, we increment the memory segment and jump out.
-
- Note that DS has to be changed to suit the routine, thus the use of CS:
- override... the CS override makes sure the data we're storing does in the
- right segment (remember that normally DS=CS, but since we're changing DS, we
- must override the normal use of DS and make it use CS so it looks in the right
- segment)
-
- At this stage our file is loaded and memseg holds the final segment of the
- file, whilst ax holds the number of bytes in the last segment. simple
- subtraction will provide the full file size thus:
-
- Loaded file size (in bytes) = ((memseg-inital segment)*64k)+ax
-
- NB: the final number will be a double word - 32 bits long, so don;t try and
- load it into a register, or even work it out this way!!
-
- After we are finished with the file we must tell DOS so. We must call the
- Close File function to tell dos to return the Handle to the unused pool and
- also to save any changes we may have written to the file physicly to disk.
- ALWAYS CLOSE THE FILE!!! Even if you get errors, CLOSE THAT FILE!! This is
- the ONLY way to avoid your program making chop suey of the disk drive's files!!
-
- Close_File: mov ah,03eh
- mov bx,handle
- int 021h
-
- The file is now closed. Again Carry reports an error, but the only possible
- error is ax=6 which is invalid handle (Caused by feeding it a handle number
- that is not in use!) otherwise AX = 0 on exit.
-
-
-
- Writing Files to disk
- ---------------------
-
- Writing a file is identical to reading it... Open the file, write the data,
- then close it... just use the different functions as listed in your ASM book.
- Use function 3d to write an existing file. Function 3c is used to write a new,
- non existant file, or to completely trash the contents of an existing file.
-
-
- I/O ports present
- -----------------
-
- What Serial and Parallel ports are there? Memory locations 0040:0000 thru
- 0040:000F hold the addresses of the first four Serial ad parallel ports viz:
-
-
- 0040:0000 | f8 03 | f8 02 | e8 03 | e8 02 | bc 03 | 78 03 | 78 02 | 00 00 |
- | COM1: | COM2: | COM3: | COM4: | LPT1: | LPT2: | LPT3: | LPT4: |
-
- So, for example, to find the address of LPT1:, simply code:
-
- push ds
- mov ax,040h
- push ax
- pop ds
- mov dx,word ptr[8]
- pop ds
-
- DX now holds the address of LPT1:. You should remember that these ports are
- 16 bit addresses, so use a 16 bit register, and also remember that in PC's the
- Most signivigant bit is stored second.
-
- If you find 00 00 stored then there are no more ports to be found - in the
- example above for example there are but 3 parallel ports. This information is
- placed by the BIOS so once again it is quite reliable, and also multitasking
- friendly to read these bytes. The only side effect is that sometimes
- non-standard serial ports will not show up here (ie serial ports at non
- standard addresses) - although drivers that usually come with these ports
- usually update this area if needed. BBS Fossil drivers are one such driver
- that will, for example.
-
-
- Reading AT CMOS Settings
- ------------------------
-
- Everyone wants to know this!!
-
- CMOS_INFO db 03fh dup(?)
-
- Read_CMOS: push ds
- pop es
- mov cx,03fh
- mov bl,00h
- mov di,offset CMOS_INFO
- cli
-
- read_in: mov dx,0070h
- mov al,bl
- out dx,al
- mov dx,0071h
- in al,dx
- stosb
- inc bl
- loop read_in
- sti
-
- This routine basicly outputs a byte reprsenting the byte in the CMOS we want
- to read to port 0070h, then reads in the byte from port 0071h. It does this
- 03fh times to read the entire CMOS to memory.
-
- The Layout of the CMOS is thus:
-
- byte# Funtion
-
- Real Time Clock Bytes
-
- 0 Current Second in BCD format
- 1 Second Alarm in BCD
- 2 Current Minuute in BCD
- 3 Minute alarm in BCD
- 4 Current hour in BCD
- 5 Hour alarm in BCD
- 6 Current day of week in BCD
- 7 Current date in BCD
- 8 Current month in BCD
- 9 Current year in BCD
-
- Status bytes
-
- a,b,c,d Status bytes A,B,C & D
-
- System configuration
-
- e Diagnostic Status
- f ShutDown Reason
- 10 Diskette drive types where:
- bit 7-4 drive 0 (A:) bit 3-0 drive 1 (B:) where
- 0000b = none
- 0001b = 360k
- 0010b = 1.2M
- 0011b = 720k
- 0100b = 1.44M
-
- 11 fixed disk drive 0 type (0 = none, 1-46 = type)
- 12 fixed disk drive 1 type (0 = none, 1-46 = type)
-
- 13 reserved
-
- 14 equipment byte where:
- bit 7-6 = number of drives, where 00b = 1, 01b = 2
- bit 5-4 = display, where:
- 00b = reserved
- 01b = cga 40 cols
- 10b = cga 80 cols
- 11b = cga in mono mode
- bit 3-2 reserved
- bit 1 = math co present
- bit 0 = diskette drive present
-
- 15 Base memory in k, low byte
- 16 Base memory in k, high byte
- 17 Extended memory in k, low byte
- 18 Extended memory in k, high byte
-
- 19 disk type of first fixed disk
- 1a disk type of second fixed disk
-
- 1b-2dh reserved
-
- 2e high byte checksum of bytes 10-2dh
- 2f low byte checksum of bytes 10-2dh
-
- 30 Low byte actual Extended memory size
- 31 high byte actual Extended memory size
-
- 32 century in BCD
-
- 33 information flag
-
- 34-3f reserved
-
- This list is based on an early phoenix BIOS. Newer bios's wil have the
- reserved areas changed, and any bytes after 2fh are fair game.. Use this info
- at your own peril because ther is no guarantee of manufacturer compatability -
- Compaq and amstrad bieng but two known misconformers. All BIOS's with "Type
- 47" user Hard Drive types will undoubtedly be non-standard too.
-
- BCD Refers to binary coded decimal number format. This format is made to store
- the numbers 0-9 only. Imagine hex, but just omit the letters.. you got BCD..
-
- Viz: 8d = 00001000 bcd
- 11d = 00101000 bcd
- 99d = 10011001 bcd
- 47d = 01000111 bcd
-
- Not as efficient bit-wize as binary (the biggest 8 bit number in BCD is 99)
- but simple to decode - split into high and low nibbles and treat as binary.
-
-
- FastWrite String to Screen
- --------------------------
-
- Sick of DOS int 21h function 09h write string? Try this one - it's a BIOS
- string writer that's about twice as fast as DOS.. you may find it worth using
- for although it's longer than dear old dos into 21, it's surely miles the
- quicker! It is designed as a "drop in" replacement for dos int 21, function
- 09, and takes the same setup, ie $ terminated string pointed to by DS:DX. All
- registers are preserved in this routine.
-
-
- write_bios_string: push ax
- push bx
- push si
- mov si,dx
-
- loop_write: lodsb
- cmp al,'$'
- jz exit_write
-
- mov ah,0eh
- mov bx,0
- int 010h
- jmp loop_write
-
- exit_write: pop si
- pop bx
- pop ax
- ret
-
-
- ------------------------------------------------------------------------------
-
- Err... well that kinda brings to a close this ASMVEG... I've brought to
- light a few routines I was meaning to hand out long ago, and added a couple
- more I just thought of :-)
-
- Next time round i'll probably have some more code in runnable form, and will
- be looking at a specific program .. i'm not sure exactly what yet :-) but it
- will be coming... Perhaps we'll look at making musical sounds and perhaps we
- will look at low level disk structures or something... i'm not exactly sure
- yet....
-
- Anyhow, until the next VEG, keep hacking!
-
- .\\erlin
-